package com.flyox.game.militarychess.client.minaclient;

import java.io.Serializable;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PublicKey;
import java.security.spec.X509EncodedKeySpec;

import javax.crypto.KeyAgreement;
import javax.crypto.SecretKey;
import javax.crypto.interfaces.DHPublicKey;
import javax.crypto.spec.DHParameterSpec;

import org.apache.mina.common.IoHandlerAdapter;
import org.apache.mina.common.IoSession;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.flyox.game.militarychess.CONSTS;
import com.flyox.game.militarychess.bean.Player;
import com.flyox.game.militarychess.client.control.MainControl;
import com.flyox.game.militarychess.client.services.DataService;
import com.flyox.game.militarychess.message.BoardBean;
import com.flyox.game.militarychess.message.ChessPackage;
import com.flyox.game.militarychess.message.Request;
import com.flyox.game.militarychess.message.Response;
import com.flyox.game.militarychess.util.Secret;
import com.flyox.game.militarychess.util.SerialUtil;

public class MinaClientHandler extends IoHandlerAdapter {
	private final Logger logger = LoggerFactory.getLogger(getClass());
	
	DataService dataService = new DataService();
	MainControl control;

	public MinaClientHandler(MainControl control) {
		this.control = control;
	}

	@Override
	public void sessionCreated(IoSession session) throws Exception {
	}

	@Override
	public void sessionOpened(IoSession session) throws Exception {
	}

	@Override
	public void messageReceived(IoSession session, Object message) throws Exception {
		ChessPackage cp = (ChessPackage) message;
		switch (cp.getCmd()) {
		case CONSTS.CMD_HELLO:
			helloRes(session, cp);
			break;
		case CONSTS.CMD_HAND_1:
			helloHand1Res(session, cp);
			break;
		case CONSTS.CMD_HAND_2:
			helloHand2Res(session, cp);
			break;
		case CONSTS.CMD_SEC:
			commonResponse(session, cp);
			break;
		case CONSTS.CMD_BOARD:
			boardResponse(session, cp);
			break;
		default:
			break;
		}
	}

	@Override
	public void sessionClosed(IoSession session) throws Exception {
	}
	@Override
    public void exceptionCaught(IoSession session, Throwable cause) throws Exception {
    	
    }

	/**
	 * 处理加密的返回信息 主动请求时返回的信息
	 * 
	 * @param control
	 * @param session
	 * @param cp
	 */
	private void commonResponse(IoSession session, ChessPackage cp) {
		try {
			byte[] content = Secret.decipherMsg(cp.getContent(), dataService.getPlayer().getDesKey());
			Serializable s = SerialUtil.deSerial(content);
			Response response = (Response) s;
			switch (response.getResponseType()) {
			case CONSTS.CMD_SEC_LOGIN:
				control.getLoginView().showLoginRes(response);
				break;
			case CONSTS.CMD_SEC_ONSEAT:
				control.getView().onSeat(response);
				break;
			case CONSTS.CMD_SEC_READY:
				control.getView().onReady(response);
				break;
			case CONSTS.CMD_SEC_EAT:
				control.getView().onEatChess(response);
				break;
			case CONSTS.CMD_SEC_MOVE:
				control.getView().onMoveChess(response);
				break;
			case CONSTS.CMD_SEC_SURRENDER:
				control.getView().resOnSurrender(response);
				break;
			case CONSTS.CMD_SEC_OVERTIME:
				control.getView().resOverTime(response);
				break;
			case CONSTS.CMD_SEC_LOGOUT:
				control.getView().onExit(response);
				break;
			default:
				break;
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * 处理广播信息
	 * 
	 * @param control
	 * @param session
	 * @param cp
	 */
	private void boardResponse(IoSession session, ChessPackage cp) {
		try {
			byte[] content = Secret.decipherMsg(cp.getContent(), dataService.getPlayer().getDesKey());
			Serializable s = SerialUtil.deSerial(content);
			BoardBean bb = (BoardBean) s;
			switch (bb.getResponseType()) {
			case CONSTS.CMD_BOARD_ONSEAT:
				control.getView().updateHallPanelDesk(bb.getChessDesk());
				break;
			case CONSTS.CMD_BOARD_READY:
				control.getView().onBoardReady(bb);
				break;
			case CONSTS.CMD_BOARD_EAT:
				control.getView().resOnBoardEat(bb);
				break;
			case CONSTS.CMD_BOARD_MOVE:
				control.getView().resOnBoardMove(bb);
				break;
			case CONSTS.CMD_BOARD_SURRENDER:
				control.getView().resOnBoardSurrender(bb);
				break;
			case CONSTS.CMD_BOARD_OVERTIME:
				control.getView().resOnBoardOverTime(bb);
				break;
			case CONSTS.CMD_BOARD_LOGOUT:
				control.getView().onBoardLogout(bb);
				break;
			case CONSTS.CMD_BOARD_MESSAGE:
				control.getView().showMsg(bb.getMessage());
				break;
			default:
				break;
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * 三次握手 第一次返回值
	 * 
	 * @param session
	 * @param cp
	 */
	private void helloRes(IoSession session, ChessPackage cp) {
		try {
			Serializable s = SerialUtil.deSerial(cp.getContent());
			Response response = (Response) s;
			if (CONSTS.RESPONSE_OK == response.getRes()) {
				String id = response.getId();
				dataService.setPlayer(new Player(id));
				logger.trace("client CMD_HELLO OK! id=" + id);
				// stept 2
				ChessPackage reqCP = new ChessPackage(CONSTS.CMD_HAND_1, dataService.getPlayer().getId());
				session.write(reqCP);
			} else {
				logger.trace("client CMD_HELLO error! Message=" + response.getMessage());
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * 三次握手 第二次返回值
	 * 
	 * @param session
	 * @param cp
	 */
	private void helloHand1Res(IoSession session, ChessPackage cp) {
		try {
			Serializable s = SerialUtil.deSerial(cp.getContent());
			Response response = (Response) s;
			if (CONSTS.RESPONSE_OK == response.getRes()) {
				Player player = dataService.getPlayer();
				KeyFactory clientKeyFac = KeyFactory.getInstance("DH");
				X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(response.getPubKeyEnc());
				PublicKey serverPubKey = clientKeyFac.generatePublic(x509KeySpec);
				player.setOtherSidePubKey(serverPubKey);// must need
				// stept2
				ChessPackage reqCP = new ChessPackage(CONSTS.CMD_HAND_2, player.getId());

				DHParameterSpec dhParamSpec = ((DHPublicKey) serverPubKey).getParams();
				KeyPairGenerator clientKpairGen = KeyPairGenerator.getInstance("DH");
				clientKpairGen.initialize(dhParamSpec);
				KeyPair clientKpair = clientKpairGen.generateKeyPair();
				player.setKeyPair(clientKpair);// must need
				KeyAgreement clientKeyAgree = KeyAgreement.getInstance("DH");
				clientKeyAgree.init(clientKpair.getPrivate());// 初始化本地key
				// 生成本地的密钥 bobDesKey
				clientKeyAgree.doPhase(serverPubKey, true);
				// 用server的公钥定位本地key,生成本地DES密钥成功"
				SecretKey clientDesKey = clientKeyAgree.generateSecret("DES");
				player.setDesKey(clientDesKey);// must need
				// 客户端的生成公共密钥 并发送给服务端, 使其生成本地密钥
				byte[] clientPubKeyEnc = clientKpair.getPublic().getEncoded();

				reqCP.setContent(clientPubKeyEnc);
				session.write(reqCP);
			} else {
				logger.trace("client CMD_HELLO error! Message=" + response.getMessage());
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * 三次握手 第三次返回值
	 * 
	 * @param control
	 * @param session
	 * @param cp
	 */
	private void helloHand2Res(IoSession session, ChessPackage cp) {
		try {
			Serializable s = SerialUtil.deSerial(cp.getContent());
			Response response = (Response) s;
			if (CONSTS.RESPONSE_OK == response.getRes()) {
				Player player = dataService.getPlayer();
				// stept2
				ChessPackage reqCP = new ChessPackage(CONSTS.CMD_SEC, player.getId());
				Request request = new Request();
				request.setCommand(CONSTS.CMD_SEC_LOGIN);
				request.setPlayerName(control.getLoginView().getJTextFieldName().getText());
				request.setPlayerPass(new String(control.getLoginView().getJPasswordField().getPassword()));
				logger.trace("client before CMD_SEC-->CMD_SEC_LOGIN name=" + request.getPlayerName() + " pass=" + request.getPlayerPass());
				byte[] serRequest = SerialUtil.enSerial(request);
				byte[] content = Secret.encryptMsg(serRequest, player.getDesKey());

				reqCP.setContent(content);
				session.write(reqCP);
			} else {
				logger.trace("client CMD_HELLO error! Message=" + response.getMessage());
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

}
